#!/usr/bin/env python
# -*- coding: utf8 -*-
from threading import Thread
import socket
import random
import sys
import traceback

from soccerbots.net import *
from soccerbots.core.structs import *


##########################  
#Classe del Servidor TCP #
##########################
class TCPThread(Thread):
    def __init__(self, socket, parent, id):
        Thread.__init__(self)
        self.socket = socket
        self.socket.setblocking(1)
        self.net = BotsSocket(self.socket)
        self.parent = parent
        self.id = id
        self.start()

    def _read_message(self):
        data = str(self.socket.recv(1024))
        if data == '': raise RuntimeError
        return data.split('\n')

    def _send_message(self, data):
        msg = '\n'.join(data)
        self.socket.send(msg)

    def _build_line(self, k, v):
        return str(k)+": "+str(v)

    def _parse_line(self, line):
        parts = line.split(':')
        k = parts[0].strip()
        v = (':'.join(parts[1:])).strip
        return k,v

    def sendAck(self,event):
        pass

    def finalize(self):
        #tancar socket close
        self.socket.close()
        self.stop()

    def debug(self, msg):
        print self.id, msg

    def run(self):
        error = False
        while not error:
            try:
                self.debug("Espero missatge")
                msg = self.net.readMessage()
                self.debug("Rebut: "+msg.type)
            
                if msg.type=="CONNECTA":
                    key,hexport = msg.getLine(0)

                    #Enviem ACK
                    msg = NetMessage('CONNECTA-ACK')
                    msg.appendValue('Id', tohexstr(self.id))
                    msg.appendValue('FLM',tohexstr(self.parent.properties['FLM']))
                    msg.appendValue('FAM',tohexstr(self.parent.properties['FAM']))
                    msg.appendValue('PR', tohexstr(self.parent.properties['PR']))
                    msg.appendValue('PP', tohexstr(self.parent.properties['PP']))
                    msg.appendValue('FT', tohexstr(self.parent.properties['FT']))
                    msg.appendValue('VLM',tohexstr(self.parent.properties['VLM']))
                    msg.appendValue('VAM',tohexstr(self.parent.properties['VAM']))
                    self.net.sendMessage(msg)

                    #Enviem el port i l'adreça al pare
                    port = int(strhex(hexport))
                    addr = self.net.getSource()[0]
                    self.parent.addDataClient(self.id, (addr, port))
                    
                elif msg.type=="INICI":
                    message = Event()
                    message.type = Event.INICI
                    self.parent.eventReceived(self.id, message)

                elif msg.type=="GOL":
                    message = Event()
                    message.type = Event.GOL
                    self.parent.eventReceived(self.id, message)

                elif msg.type=="XIULET":
                    message = Event()
                    message.type = Event.XIULET
                    self.parent.eventReceived(self.id, message)

                elif msg.type=="FALTA":
                    message = Event()
                    message.type = Event.FAULT
                    k,team = msg.getLine(0)
                    k,fault = msg.getLine(1)
                    message.team = int(team)
                    message.fault = int(fault)
                    self.parent.eventReceived(self.id, message)
                
                elif msg.type=="FIPART":
                    message = Event()
                    message.type = Event.FIPART
                    self.parent.eventReceived(self.id, message)

                elif msg.type=="FIPARTIT":
                    message = Event()
                    message.type = Event.FIPARTIT
                    self.parent.eventReceived(self.id, message)

                else:
                    self.debug('Received an incorrect package !')
            except:
                print 'Un client ha sortit !'
                traceback.print_exc(file=sys.stdout)
                error = True

    def golAck(self, m):
        msg = NetMessage('GOL-ACK')
        msg.appendValue('Marcador',str(m[0])+'-'+str(m[1]))
        self.net.sendMessage(msg)
    
    def faultAck(self, team, type):
        msg = NetMessage('FALTA-ACK')
        msg.appendValue('Equip',team)
        msg.appendValue('Tipus',type)
        self.net.sendMessage(msg)
       
    def fiPartAck(self):
        msg = NetMessage('FIPART-ACK')
        self.net.sendMessage(msg)

    def fiPartitAck(self):
        msg = NetMessage('FIPARTIT-ACK')
        self.net.sendMessage(msg)

##########################  
#Classe del Servidor UDP #
##########################

class UDPThread(Thread):
    def __init__(self, parent):
        Thread.__init__(self)
        self.socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) 
        self.socket.bind(('',10000))
        self.net = BotsSocket(self.socket)
        self.parent = parent
        self.clients = {} 
    
    #Crear un nou client UDP
    def addClient(self, id, addr):
        self.clients[id] = addr
    
    #Envia el estat de la partida als clients
    def sendState(self, time, state):
        msg = NetMessage()
        msg.append("%x"%int(time*1000))
        for line in state.getLines():
            msg.append(line)

        for client in self.clients.values():
            self.net.sendMessage(msg, client)

    #Finalitza el thread
    def finalize(self):
        #tancar socket close
        self.stop()
    
    # Esperar els moviments dels fills
    def run(self):
        error = False
        while not error:
            try:
                msg = self.net.readPlainMessage()
                mov = TeamMovement.parseFromLines(msg.lines)
                print 'Received movements from',mov.id
                self.parent.movementReceived(mov)
            except:
                traceback.print_exc(file=sys.stdout)
                error = True


######################################################################  
#Classe del Servidor on s'arranca el servidors TCP i els servidor UDP#
######################################################################
class Server(Thread):
    #Inicialitzacions de les varibales i dels sockets necessaris
    def __init__(self, app, properties):
        Thread.__init__(self)
        self.servsocket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        self.servsocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.players = {} # dict indexed by client ID
        self.app = app
        self.properties = properties
        
        self.data = UDPThread(self)
        self.data.start()

    #Funcio per incloure un client UDP
    def addDataClient(self, id, addr):
        self.data.addClient(id, addr)
        self.app.clientJoined(id)
    
    # Enviar l'estat inicial a un client.
    def sendInitState(self, id, team, atack, state):
        msg = NetMessage('ESTATINI')
        msg.appendValue('Equip', team)
        msg.appendValue('Atancant', atack)
        lines = state.getLines()
        for line in lines:
            msg.append(line)
        self.players[id].net.sendMessage(msg)

    def sendState(self, time, state):
        self.data.sendState(time, state)
    
    #Funcio que crida el servidors TCP per informa de l'arribada d'un Event
    #La funció envia confirmacio del event als clients i informa al controlador
    #de que li arribar un event
    def eventReceived(self, id, event):
        self.app.eventReceived(id, event)

    def movementReceived(self, movement):
        self.app.movementReceived(movement)

    #Crear fill que mira les connexions
    def run(self):
        print "Binding......"
        self.servsocket.bind(("", 10000))
        self.servsocket.listen(1)
        print "Listening......"
        id = 20
        error = False
        while not error:
            try:
                socket,a = self.servsocket.accept()
                client_thread = TCPThread(socket, self, id)
                self.players[id] = client_thread
                id += 1
            except:
                error = True

    def golAck(self, m):
        for client in self.players.values():
            client.golAck(m)

    def faultAck(self, team, type):
        for client in self.players.values():
            client.faultAck(team, type)
    
    def fiPartAck(self):
        for client in self.players.values():
            client.fiPartAck()

    def fiPartitAck(self):
        for client in self.players.values():
            client.fiPartitAck()

    def finalize(self):
        for player in self.players.values():
            player.socket.close()
        self.servsocket.close()
